Envisage ZK 6: An Annotation Based Composer For MVC
Simon Pai, Engineer, Potix Corporation
January 27, 2011
ZK 6
The Story
Think of an extremely ordinary scenario, where you want to clear all the texts in a form by clicking a button.
While you see the screen shot, you may have already come up with an implementation in mind. For example:
public class SomeFormController extends GenericForwardComposer {
Textbox usenameTb;
Textbox passwordTb;
Textbox retypepwTb;
// ...
// ...
Textbox memoTb;
public void onClick$clearBtn(Event event) {
usenameTb.setValue("");
passwordTb.setValue("");
retypepwTb.setValue("");
// ...
// ...
memoTb.setValue("");
}
}
But wait, no.
The unordinary part of the story is here: this feature is actually implemented by just 9 lines of code.
public class FormController extends SelectorComposer {
@Wire("textbox, intbox, decimalbox, datebox")
List<InputElement> inputs;
@Listen("onClick = button[label='Clear']")
public void onClear(MouseEvent event) {
for(InputElement i : inputs) i.setText("");
}
}
These are what we foresee in ZK 6: leveraging Annotation power from Java 1.5, and introduction to some new techniques.
In this Small Talk we are going to reveal two new weapons: Selector and SelectorComposer.
The jQuery/CSS3-like Component Selector
In the previous example, Selector is shown as a part of the parameters in Annotation @Wire
and @Listen
.
@Wire("textbox, intbox, decimalbox, datebox")
@Listen("onClick = button[label='Clear']")
The concept is simple: Selector is a pattern string that matches nodes in a Component tree.
In other words, by giving a Selector string, you can specify a collection of Components from a ZUL file.
// Collects all the textboxes, intboxes, decimalboxes, and dateboxes as a List and wire to inputs
@Wire("textbox, intbox, decimalbox, datebox")
List<InputElement> inputs;
// Collects all the buttons whose label is "Clear", and adds EventListeners for them
@Listen("onClick = button[label='Clear']")
public void onClear(MouseEvent event) {
// ...
}
If you know jQuery or CSS selector, this is exactly their counterpart on server side.
Syntax
The syntax of Selector is closely analogous to CSS3 selector.
Component type, class, attribute, pseudo class are used to describe properties of a component. For example:
// Matches any Button component
"button"
// Matches any Component with ID "btn"
"#btn"
// Matches any Button with ID "btn"
"button#btn"
// Matches any Button whose label is "Submit"
"button[label='Submit']"
Combinators are used to describe relations between components. For example:
// Matches any Button who has a Window ancestor
"window button"
// Matches any Button whose parent is a Window
"window > button"
// Matches any Button whose previous sibling is a Window
"window + button"
// Matches any Button who has a Window as a senior sibling
"window ~ button"
// Matches any Button whose parent is a Div and grandparent is a Window
"window > div > button"
SelectorComposer
SelectorComposer is analogous to GenericForwardComposer. But instead of wiring variables by naming convention, the new composer wires them by annotation and specifies the Components by Selector.
public class MyComposer extends SelectorComposer {
// If the field is a Collection, the composer will wire all Components matched by the selector
@Wire("label")
private List<Label> labelList;
// Same for Array
@Wire("label")
private Label[] labelArray;
// If the field is not a Collection or Array, the first matched Component is wired to the field
@Wire("label[value='zk']")
private Label label1;
// If selector string is not given, it will attempt to wire implicit objects by name or fellows by ID.
@Wire
private Desktop desktop;
@Wire
private Button clearBtn;
}
Event listening is handled in a similar way. However, instead of forwarding the events, it adds the method to the EventListener of the target Components directly.
public class MyComposer extends SelectorComposer {
// Like auto-forwarding, methods annotated with @Listen will be added to the event listeners of the components described by selector.
@Listen("onClick = button#btn")
public void onPressButton(Event event) {
// The event here will be the original event, not ForwardEvent!
}
// The event listener will be added to ALL the components that match the selector, not just the first match
@Listen("onClick = #myGrid > rows > row")
public void onClickAnyRow(MouseEvent event) {
// ...
}
// You can specify multiple event types
@Listen("onClick = button[label='Submit']; onOK = textbox#password")
public void onSubmit(Event event) {
// ...
}
}
More about Selector
You can also use Selector independently.
For example:
Window win;
// returns a list of components, containing all labels in the page
Selectors.find(page, "label");
// returns all components with id "myId" under the Window win. (including itself)
Selectors.find(win, "#myId");
// returns all components whose .getLabel() value is "zk" (if applicable)
Selectors.find(page, "[label='zk']");
// returns all captions whose parent is a window
Selectors.find(win, "window > caption");
// returns all buttons and toolbarbuttons
Selectors.find(page, "button, toolbarbutton");
// you can assemble the criteria:
// returns all labels, whose parent is a window of id "win", and whose value is "zk"
Selectors.find(page, "window#win > label[value='zk']");
Comparison with CSS3 Selector
Syntax | In CSS 3 Selector | In Component Selector | Comment |
---|---|---|---|
tagname
|
DOM element type | Component type | |
#id
|
DOM ID | Component ID | |
.class
|
CSS class | SClass / ZClass | |
[attr='value']
|
DOM attribute | getAttr() or dynamic attribute | If getAttr() is not a method on such component, it is skipped |
:pseudo-class
|
Pseudo class | Pseudo class | :root, :empty, :first-child, :last-child, :only-child, :nth-child(), :nth-last-child()
|
::pseudo-element
|
Pseudo element | N/A | |
> + ~
|
Combinator | Combinator | Identical to CSS 3 combinators |
Comments
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License. |